﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Snowdreamist.ComponentModel.Composition.Navigation;
using Snowdreamist.ComponentModel.Plugin;

namespace Snowdreamist.Windows.Navigation.Primitives
{
    public class NavigationHandlerRepository
    {
        private static readonly ImportedPluginProperty<INavigationHandler, INavigationHandlerMetadata> NavigationHandlersProperty
            = ImportedPluginProperty.Create<INavigationHandler, INavigationHandlerMetadata>(new ContractMetadata(
                new Guid("7DE7BC26-6620-46F7-A2B1-CC36CF79005B"), null, typeof(INavigationHandler), CandidateMode.ZeroOrMore, typeof(NavigationHandlerRepository)
            ));

        public NavigationHandlerRepository()
        {
            // register composition handlers
            var composedHandlers = this.NavigationHandlers;
            if (composedHandlers != null)
                foreach (var handler in composedHandlers)
                {
                    if (handler.Plugin != null && handler.Metadata != null)
                    {
                        try
                        {
                            this.Register(handler.Plugin, handler.Metadata);
                        }
                        catch (Exception error)
                        {
                            // log
                        }
                    }
                }
        }

        public IEnumerable<INavigationHandler> Handlers
        {
            get
            {
                return this._handlerDict.Values.Select(p => p.Handler).ToArray();
            }
        }

        public void Register(INavigationHandler handler, INavigationHandlerMetadata metadata)
        {
            if (handler == null)
                throw new ArgumentNullException("handler");
            if (metadata == null)
                throw new ArgumentNullException("metadata");
            if (string.IsNullOrWhiteSpace(metadata.Name))
                throw new ArgumentException("Invalid navigation handler name");
            lock (this)
            {
                if (this._handlerDict.ContainsKey(metadata.Name))
                    throw new ValueExistsException("Name already exists");
                this._handlerDict.Add(metadata.Name, new HandlerWrapper()
                {
                    Handler = handler,
                    Metadata = metadata
                });
            }
        }

        public void UnRegister(INavigationHandler handler)
        {
            if (handler == null)
                throw new ArgumentNullException("handler");
            lock (this)
            {
                var removingHandlers = this._handlerDict.Where(p => p.Value.Handler == handler);
                if (removingHandlers.Count() > 0)
                {
                    foreach (var removingHandler in removingHandlers)
                        this._handlerDict.Remove(removingHandler.Key);
                }
            }
        }

        public void UnRegister(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentException("Invalid navigation handler name");
            lock (this)
            {
                if (this._handlerDict.ContainsKey(name))
                    this._handlerDict.Remove(name);
            }
        }

        public void Clear()
        {
            lock (this)
            {
                this._handlerDict.Clear();
            }
        }

        public INavigationHandler GetHandler(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentException("Invalid navigation handler name");

            HandlerWrapper wrapper = null;

            if (this._handlerDict.TryGetValue(name, out wrapper))
                return wrapper.Handler;
            else
                return null;
        }

        #region private

        private readonly Dictionary<string, HandlerWrapper> _handlerDict = new Dictionary<string, HandlerWrapper>();

        private IEnumerable<ImportedPlugin<INavigationHandler, INavigationHandlerMetadata>> NavigationHandlers
        {
            get
            {
                return NavigationHandlersProperty.GetValuesWithMetadata(this);
            }
        }

        #endregion private
    }

    internal class HandlerWrapper
    {
        public INavigationHandler Handler { get; set; }

        public INavigationHandlerMetadata Metadata { get; set; }
    }
}